package au;

import java.awt.Point;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import Luxand.FSDK;

public abstract class AU implements Serializable {

	private static final long serialVersionUID = 1L;
	
	protected List<Point> p;

	// Public API
	
	/*
	 * List of all AU labels
	 * Switch block contains the best training parameters for each AU
	 * 
	 * _N_, SURPRISE, LFAU_16, LFAU_25, LFAU_26, LFAU_27, LFAU_12, LFAU_12L, LFAU_12R, LFAU_12LW, LFAU_14, LFAU_20, LFAU_15, LFAU_17, LFAU_22, LFAU_18, LFAU_23, LFAU_24, LFAU_28, LFAU_10, LFAU_9, LFAU_34, UFAU_1, UFAU_2, UFAU_4, UFAU_43, UFAU_44;
	 * 
	 * */
	public enum Label {
		_N_, SURPRISE, LFAU_16, LFAU_25, LFAU_26, LFAU_27, LFAU_12, LFAU_12L, LFAU_12R, LFAU_12LW, LFAU_14, LFAU_20, LFAU_15, LFAU_17, LFAU_22, LFAU_18, LFAU_23, LFAU_24, LFAU_28, LFAU_10, LFAU_9, LFAU_34, UFAU_1, UFAU_2, UFAU_4, UFAU_43, UFAU_44;
		public String getParams() {
			
			switch (this) {
			case UFAU_1:
				return "-t 2 -c 50 -g 0.01";
			case UFAU_2:
				return "-t 2 -c 200 -g 0.00001"; // 71/38
				//return "-t 2 -c 10 -g 0.00001"; 
			case LFAU_12:
				return "-t 2 -c 300 -g 0.01";
			case LFAU_23:
				return "-t 2 -c 600 -g 0.00001";
			case LFAU_26:
				return "-t 2 -c 600 -g 0.00001";
			case SURPRISE:
				return "-t 2 -c 500 -g 0.00001";
			default:
				return "";
			}
		}
	};
	
	public enum ActiveLabel {

		LFAU_26, LFAU_12, LFAU_23, UFAU_1, UFAU_2;
	}

	/*
	 * AU label - different classes inheriting AU can have the same label, e.g. AU_12_left and AU_12_right
	 * @return - one of the labels from enum Label
	 * 
	 * */
	public abstract Label getLabel();
	
	/*
	 * Returns a list of AU labels that can be used to negative tests, e.i. they are the opposite of the context AU
	 * 
	 * */
	public abstract List<String> getNegatives();

	/*
	 * Returns the feature vector
	 * IMPORTANT!!! The implementation of this method has to begin with "p = points;" because of base units
	 * 
	 * */
	public abstract double[] getFeatureVectorByFacialFeatures(List<Point> points);

	// ********* Private *********
	
	// Base units

	private double horizontalBaseUnit() {
		return Math.abs(p.get(0).x - p.get(1).x);
	}

	private double verticalBaseUnit() {
		return Math.abs(p.get(22).y - p.get(42).y);
	}

	// Geometry

	public double dh(Point a, Point b) {
		return (Math.abs(a.y - b.y)) / this.verticalBaseUnit();
	}

	public double dw(Point a, Point b) {
		return (Math.abs(a.x - b.x)) / this.horizontalBaseUnit();
	}

	public double distance(Point a, Point b) {
		return (Math.sqrt(Math.abs(a.y - b.y) * Math.abs(a.y - b.y)
				+ Math.abs(a.x - b.x) * Math.abs(a.x - b.x)))
				/ this.horizontalBaseUnit();
	}

	public double angle(Point current, Point center, Point previous) {
		return Math.toDegrees(Math.atan2(current.x - center.x, current.y
				- center.y)
				- Math.atan2(previous.x - center.x, previous.y - center.y));
	}

}
